<?php


namespace app\api\controller;

use app\common\controller\Api;
use app\api\controller\send;
use think\Request;

class Lfpage45Andcodconvert extends Api
{
    // 无需登录的接口,*表示全部
    protected $noNeedLogin = ['*'];
    // 无需鉴权的接口,*表示全部
    protected $noNeedRight = ['*'];

    // 定义mask码，用于转换
    /*
     * LF 可讀寫器可讀寫是 Page4、5 兩個信息，一般通用讀卡器會將這兩個信息顯示為 COD 10 進制數字
     * LFPage4、5码：Page 4 : FFBD FD7E  Page 5 : 3785 19EA
     * COD 10 進制數字為 3889022263
     * 轉換為 16 進制為  E7CDC537
     * 前方增加兩個 16 進制碼 77 為 廠商代碼: 77 E7CD C537
     * 最终结果：Page 4 : FFBD FD7E  Page 5 : 3785 19EA  =》 77 E7CD C537（HEX16进制数，带77廠商代碼） 3889022263（DEC10进制数）
     */

    /*
     * LFPage4、LFPage5转换为COD码，就是显示码
    */
    // http://mg.acciot.com.tw/api/Lfpage45andcodconvert/LFPage45ToCOD?LFPage45=FFBDFD7E378519EA
    // http://localhost:9100/api/Lfpage45andcodconvert/LFPage45ToCOD?LFPage45=FFF4B100C60C1414
    public function LFPage45ToCOD($LFPage45)
    {
        if(strlen($LFPage45) > 16)
        {
            return '';
        }
        else
        {
            // 位数不足，补充0
            // 如果二进制不是4的倍数，就要在前面用0补位
            for(; strlen($LFPage45) < 16; )
            {
                $LFPage45 = '0' . $LFPage45;
            }
        }
        $str1 = substr($LFPage45, 0, 8);
        $str2 = substr($LFPage45,8,8);
        $strBin1 = base_convert($str1, 16, 2);
        $strBin2 = base_convert($str2, 16, 2);
        // 如果二进制不是4的倍数，就要在前面用0补位
        if(strlen($strBin1) / 4 != 0)
        {
            for($i = 0; $i < strlen($strBin1)%4; $i++)
            {
                $strBin1 = '0' . $strBin1;
            }
        }
        // 如果二进制不是4的倍数，就要在前面用0补位
        if(strlen($strBin2) / 4 != 0)
        {
            for($i = 0; $i < strlen($strBin2)%4; $i++)
            {
                $strBin2 = '0' . $strBin2;
            }
        }
        $strBin = $strBin1 . $strBin2;
        // 从二进制字符串第20位开始，包含前面厂家代码，循环到末尾后第6位结束
        $strLastBin = '';  // 最后合成的二进制字符串
        for($i = 20; $i<64-6; $i++)
        {
            //echo '+++' . $strBin[$i] . "<br>";
            // 每5位是一段
            if((($i-9+1) % 5) == 0)
            {
                continue;
            }
            $strLastBin .= $strBin[$i];
        }
        $strLastDEC = strtoupper(base_convert($strLastBin, 2, 10));

        return $strLastDEC;
    }

    /*
     * COD转换为LFPage4、LFPage5码
     * 校验方法就是：
     * 1、横向：$COD每个16进制数后面要加效验码，奇数个1就是1，偶数个1就是0。
     * 2、纵向：然后所有的16进制的每个bit位，做纵向奇偶校验，包含第1个横向校验码，也做同样的效验。
     * 3、长度：9个前缀+50(2*5=10(厂家代码), 8x5=40，即8个16进制的COD码，每个是5个bits)+5(纵向效验位 5个bits) = 64bits（16个16进制字符)
    */
    // http://mg.acciot.com.tw/api/Lfpage45andcodconvert/CODToLFPage45?COD=1076800314&oldLFPage45=FFC629017B4B9A80
    // http://localhost:9100/api/Lfpage45andcodconvert/CODToLFPage45?COD=1076800314&oldLFPage45=FFC629017B4B9A80
    public function CODToLFPage45($COD, $oldLFPage45)
    {
        // $oldLFPage45是16位长度的16进制数
        if(strlen($oldLFPage45)>16)
        {
            return '';
        }
        $CODNew= $COD;
        // $COD码要做横向和纵向奇偶效验
        $strvarify = base_convert($CODNew, 10, 16);
        // $COD是8位长度（16进制）的10进制数
        if (strlen($strvarify) > 8) {
            // 只取后面8位字符串
            $CODNew = substr($strvarify, -8);
            $CODNew = base_convert($CODNew, 16, 10);
        }
        $strBinOld = base_convert($CODNew, 10, 2);    // 共32bits

        // 位数不足，补充0
        // 如果二进制不是4的倍数，就要在前面用0补位
        for(; strlen($strBinOld) < 32; )
        {
            $strBinOld = '0' . $strBinOld;
        }
        // 转成2进制的前缀，共19个bits
        $Pre2Chars = base_convert($oldLFPage45, 16, 2);
        if($Pre2Chars)
        {
            for(; strlen($Pre2Chars) < 64; )
            {
                // 前面补0
                $Pre2Chars = '0' . $Pre2Chars;
            }
        }
        $strPreBin = substr($Pre2Chars, 0, 19);    //共19个bits

        $strBinNew = '';    // 用于保存转换后的COD码
        $odd_even_count = 0;    // 用来统计1的个数

        // 横向统计效验位
        for ($i = 0; $i <= strlen($strBinOld); $i++) {
            //先每隔4个bit，插入一个横向奇偶效验位
            if ($i != 0 and ($i % 4) == 0) {
                // 要判断奇偶效验位，在后面插入效验位
                if (($odd_even_count % 2) == 0) {
                    // 偶数个1，就是0
                    $strBinNew .= '0';
                } else {
                    // 奇数个1，就是1
                    $strBinNew .= '1';
                }
                // 最后一次，添加了效验位，就要结束横向效验
                if ($i == strlen($strBinOld)) {
                    break;
                }
                // $odd_even_count要重新开始计数
                $odd_even_count = 0;
            }
            if ($strBinOld[$i] == '1') {
                $odd_even_count++;
            }
            $strBinNew .= $strBinOld[$i]; // 字符数组从0开始
        }

        // 纵向统计效验位
        // $strBinNew应该由原来$strBinOld的32bits，变成40bits
        $odd_even_count = 0;    // 用来统计1的个数
        // 前面加上10位2进制厂家代码
        $strBinNew = substr($strPreBin, -10) . $strBinNew;
        // 外循环共5列
        for ($i = 0; $i < 5; $i++) {
            // 内循环共10行
            for ($j = 0; $j < 10; $j++) {
                if ($strBinNew[$i + $j * 5] == '1') {
                    $odd_even_count++;
                }
            }
            // 内循环结束就是每列已经统计完毕，要填充该列的效验位
            if (($odd_even_count % 2) == 0) {
                $strBinNew .= '0';
            } else {
                $strBinNew .= '1';
            }
            // $odd_even_count清0，重新统计下一列
            $odd_even_count = 0;
        }
        // 加上前缀就是最后的LFpage4、page5的码
        $strBinNew = substr($strPreBin, 0, 9) . $strBinNew;
        $str1 = substr($strBinNew, 0, 32);
        $str2 = substr($strBinNew, 32, 32);
        $str1Hex = base_convert($str1, 2, 16);
        $str2Hex = base_convert($str2, 2, 16);
        $strLFpage45Hex = strtoupper($str1Hex . $str2Hex);
        return $strLFpage45Hex;
    }
}